/*
 * TOBIAS ER LORT
 * MATHIAS ER MERE LÅÅÅÅRT
 */

function asynconload(f) {
	var old = window.onload;
	window.onload = function() {
		if(old) old();
		setTimeout(f,1000);
	}
}

function sysout(str) {
	output("system", str);
}

function cacheable(s) {

	function cachefunction(f) {
		var cache = null;
		return function() {
			if(!cache)
				cache = f();
			return cache;
		};
	};	
	
	s.clearConnections = function(){};
	s.getName 		= 	cachefunction(s.getName);	
	s.getArguments 	= 	cachefunction(s.getArguments);	
	s.getPosition 	= 	cachefunction(s.getPosition);	
	s.isOutputter 	= 	cachefunction(s.isOutputter);
	
	return s;
	
}

function output(id, str) {
	d("out").innerHTML = "<span style='" + (id == "system"? "" : "color: black;") + "'>&lt;" + id + "&gt; " + str + "<span><br>" + d("out").innerHTML;
}

function d(id) {
	return document.getElementById(id);
}

function t(id, tag, i) {
	return d(id).getElementsByTagName(tag)[i];
}

function clearServiceGUI() {
	t("modal", "input", 0).value = "";
	t("modal", "input", 1).value = "";
	t("modal", "textarea", 0).value = "";	
}

function connectServices(s1,s2,arg) {
	if(locals[s1]) {
		var a = serviceObjects[s1];
//		sysout('will add connection to ' + a.getName())
		a.addConnection(s1,s2,arg);
	}
//	sysout('connecting ' + s1 + "  " + s2 + "  " + arg + "  local: " + (!!locals[s1]) );

}

function disconnectServices(s1,s2,arg) {
	serviceObjects[s1].removeConnection(s1+'-'+s2+'-'+arg);
}

function showServiceGUI(pos) {
	clearServiceGUI();
	d("modal").style.display = "";
	
	d("cr").onclick = function() {
		var s = {};
		
		var serviceName = t("modal", "input", 0).value;
		var serviceArgs = t("modal", "input", 1).value == ""? [] : t("modal", "input", 1).value.split(",");
/*		var service
		for (var arg in serviceArgs) {
			if (serviceArgs[arg].substr(0, 2) == "__") {
				
			}
		}*/
		eval("var serviceLogic = function(" + t("modal", "input", 1).value + ") {\n"+t("modal", "textarea", 0).value+"\n}");
		var serviceOutput = (""+serviceLogic).indexOf("return ") != -1;

		s.run = serviceLogic;
		
		s.connections = {};

		var inputBuffer = {};
		
		function isBufferFull() {
			for(var i = 0; i < serviceArgs.length; i++)
				if(inputBuffer[serviceArgs[i]] == null)
					return false;
			return true;
		}
		
		s.clearConnections = function() {
//			sysout('clearing all for ' + serviceName);
			s.connections = {};
		}
		
		s.removeConnection = function(id) {
			delete s.connections[id];
		}
		
		s.addConnection = function(s1,s2,arg) {
//			sysout('connecting ' + s1 + ' ' + s2 + ' ' + arg)
			s.connections[s1+'-'+s2+'-'+arg] = {
				source: serviceObjects[s1],
				sink: serviceObjects[s2],
				argument: arg,
				conarray: [s1,s2,arg]
			};
			
		};
		
		s.produce = function() {
//			sysout('is buffer full: ' + isBufferFull())
			if(isBufferFull()) {
				var curArgs = [];
				for(var i = 0; i < serviceArgs.length; i++) {
					try {
						curArgs[i] = inputBuffer[serviceArgs[i]].arg;
						connector.connectionIdle(inputBuffer[serviceArgs[i]].conarray);
					}
					catch(err) {
						inputBuffer = {}; //to prevent bugs
						return;
					}
				}
			
				var res = serviceLogic.apply(s, curArgs);
				
				
//				sysout('sending...')
				for(var conid in s.connections) {
//					sysout('sending to ' + conid);
					var c = s.connections[conid];
					
					connector.connectionActive(c.conarray);
					c.sink.setInput(c.conarray, c.argument, res);
					
					
				}
				
				inputBuffer = {};
			}
		}
		
		
		s.setInput = function(carr, argName, val) {
			inputBuffer[argName] = {arg: val, conarray: carr};
			s.produce();
		}
		s.getName = function() {
			return serviceName;
		}
		s.getArguments = function() {
			return serviceArgs;
		}
		s.isOutputter = function() {
			return serviceOutput;
		}
		s.getPosition = function() {
			return pos;
		}
		
		locals[serviceName] = s;
		var serviceTemplates = [];
		CFPH.Draw.addService(pos[0],pos[1],serviceName,serviceArgs,serviceTemplates,serviceOutput,s,function() {
			s.produce();
		});
		
		hideServiceGUI();
		
		TITT.register(s, "services", serviceName);
		
		serviceObjects[serviceName] = s;
		
		connector.serviceAdded(updater.id, serviceName);
	}
	
	d("cs").onclick = hideServiceGUI;
}

function hideServiceGUI() {
	d("modal").style.display = "none";
	clearServiceGUI();
}

function getServiceObjects(services) {
	var ret = [];
	for(var i = 0; i < services.length; i++) {
		if(locals[services[i]] != null)
			ret[i] = locals[services[i]];
		else if(remotes[services[i]] != null)
			ret[i] = remotes[services[i]];
		else {
			remotes[services[i]] = cacheable(TITT.createProxy("services", services[i]));
			ret[i] = remotes[services[i]];
		}	
	}
	return ret;
}

var locals = {};
var remotes = {};

var serviceObjects = {};

var updater = {
	updateActives: function(connections) {
		CFPH.Draw.deactivateAllConnections();
	
		for(var i = 0; i < connections.length; i++) {
			CFPH.Draw.activateConnection(connections[i][0],connections[i][1],connections[i][2]);
		}
		
	},
	update: function(services, connections) {
		var ss = getServiceObjects(services);
		serviceObjects = {};
				
		var data = [];
		
//		sysout('UPDATE: 112')
		for(var i = 0; i < ss.length; i++) {
			
			ss[i].clearConnections();
			var spos = ss[i].getPosition();
			var sid = ss[i].getName();
			var sargs = ss[i].getArguments();
			var soutput = ss[i].isOutputter();
			
			data[i] = {
				pos: spos,
				id: sid,
				args: sargs,
				output: soutput,
				service: ss[i]
			}
			serviceObjects[sid] = ss[i];
		}

		CFPH.Draw.destroyAll();

//		sysout('UPDATE: 134')
		for(var i = 0; i < ss.length; i++) {
//			sysout("args " + data[i].args);
			var serviceTemplates = [];
			CFPH.Draw.addService(data[i].pos[0], data[i].pos[1] ,data[i].id, data[i].args, serviceTemplates, data[i].output, null, (function(service) {
				return function() {
					service.produce();
				}
			})(ss[i]));
		}
		
//		sysout('UPDATE:  connections ' + connections);
//		sysout('connections ' + connections.length);
		
		for(var i = 0; i < connections.length; i++) {
//			sysout("adding " + connections[i]);
			connectServices(connections[i][0], connections[i][1], connections[i][2]);
			CFPH.Draw.createConnection(connections[i][0], connections[i][1], connections[i][2]);
		}
//		alert("done");
	}
}

var connector;

asynconload(function() {

	var hei = document.body.clientHeight- 70;
	
	d("out").style.height = hei + "px";

	d("loader").style.display = "none";
	d("main").style.display = "";

	d("kanvas").onmousedown = function(e) {	
		if(e.shiftKey) {
			var x = e.pageX, y = e.pageY; //not cross-browser
			var pos = [x - d("kanvas").offsetLeft - 50, y - d("kanvas").offsetTop - 20];

			showServiceGUI(pos);
		}
	}
	
	//redundant
	var paper = Raphael("kanvas", document.body.clientWidth - 300, document.body.clientHeight- 70);
	
	CFPH.Draw.init(paper);
	
	CFPH.Draw.setListener({
		inOutConnected: function(from, to, paramName) {
			if(CFPH.Draw.connectionExists(from, to, paramName)) {
				disconnectServices(from, to, paramName);
				CFPH.Draw.removeConnection(from, to, paramName);
				connector.connectionRemoved(updater.id, [from,to,paramName]);
			}
			else {
				connectServices(from, to, paramName);
				CFPH.Draw.createConnection(from, to, paramName);
				connector.connectionAdded(updater.id, [from,to,paramName]);
			}
		}
	});
	
	connector = TITT.createProxy("connector");
	
	updater.id = connector.generateID();
	
	TITT.register(updater, "connector", updater.id);
	
	updater.update(connector.getAllServices(), connector.getAllConnections());
	
	sysout("onload done id=" + updater.id);
});

